home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Graphics / GraphicsWrap / Source / ListMatrix.m < prev    next >
Text File  |  1991-09-15  |  11KB  |  381 lines

  1. #import <appkit/Application.h>
  2. #import <appkit/Cell.h>
  3. #import <appkit/Window.h>
  4. #import <appkit/timer.h>
  5. #import <dpsclient/psops.h>
  6. #import <dpsclient/wraps.h>
  7. #import <objc/List.h>
  8.  
  9. #import "ListMatrix.h"
  10.  
  11. @implementation ListMatrix
  12. int selectedCount;
  13. id tList;
  14.  
  15. /* #defines stolen from Draw */
  16.  
  17. #define startTimer(timer) if (!timer) timer = NXBeginTimer(NULL, 0.1, 0.01);
  18.  
  19. #define stopTimer(timer) if (timer) { \
  20.     NXEndTimer(timer); \
  21.     timer = NULL; \
  22. }
  23.  
  24. #define MOVE_MASK NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK
  25.  
  26. - cellListIncludeAll:(BOOL) all
  27. {
  28.   if (tList) [tList free];
  29.  
  30.   tList = [[List alloc] init];
  31.   [self sendAction:@selector(addToList:) to:self forAllCells:all];
  32.   if ([tList count])
  33.     return tList;
  34.  
  35.   return 0;
  36. }
  37.  
  38. - (BOOL)addToList:sender;
  39. {
  40.   [tList addObject:sender];
  41.   return YES;
  42. }
  43.  
  44. /* instance methods */
  45. - init
  46. {
  47.   [super init];
  48.   selectedCount=0;
  49.   tList=0;
  50.   return self;
  51. }
  52.  
  53. - free
  54. {
  55.     [matrixCache free];
  56.     [cellCache free];
  57.     
  58.     return [super free];
  59. }
  60.  
  61. - mouseDown:(NXEvent *)theEvent
  62. {
  63.     NXPoint        mouseDownLocation, mouseUpLocation, mouseLocation;
  64.     int            eventMask, row, column, newRow;
  65.     NXRect        visibleRect, cellCacheBounds, cellFrame;
  66.     id            matrixCacheContentView, cellCacheContentView;
  67.     float        dy;
  68.     NXEvent        *event, peek;
  69.     NXTrackingTimer    *timer = NULL;
  70.     BOOL        scrolled = NO;
  71.     
  72.   /* if the Control key isn't down, show normal behavior */
  73.     if (!controlDrag || (!(theEvent->flags & NX_CONTROLMASK))) {
  74.       return [super mouseDown:theEvent];
  75.     }
  76.     
  77.   /* prepare the cell and matrix cache windows */
  78.     [self setupCacheWindows];
  79.     
  80.   /* we're now interested in mouse dragged events */
  81.     eventMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK];
  82.  
  83.   /* find the cell that got clicked on and select it */
  84.     mouseDownLocation = theEvent->location;
  85.     [self convertPoint:&mouseDownLocation fromView:nil];
  86.     [self getRow:&row andCol:&column forPoint:&mouseDownLocation];
  87.     activeCell = [self cellAt:row :column];
  88.     [self selectCell:activeCell];
  89.     [self getCellFrame:&cellFrame at:row :column];
  90.     
  91.   /* do whatever's required for a single-click */
  92.     [self sendAction];
  93.     
  94.   /* draw a "well" in place of the selected cell (see drawSelf::) */
  95.     [self lockFocus];
  96.     [[self drawSelf:&cellFrame :1] unlockFocus];
  97.     
  98.   /* copy what's currently visible into the matrix cache */
  99.     matrixCacheContentView = [matrixCache contentView];
  100.     [matrixCacheContentView lockFocus];
  101.     [self getVisibleRect:&visibleRect];
  102.     [self convertRect:&visibleRect toView:nil];
  103.     PScomposite(NX_X(&visibleRect), NX_Y(&visibleRect),
  104.             NX_WIDTH(&visibleRect), NX_HEIGHT(&visibleRect),
  105.         [window gState], 0.0, NX_HEIGHT(&visibleRect), NX_COPY);
  106.     [matrixCacheContentView unlockFocus];
  107.  
  108.   /* image the cell into its cache */
  109.     cellCacheContentView = [cellCache contentView];
  110.     [cellCacheContentView lockFocus];
  111. //    [cellCacheContentView getFrame:&t];
  112. //    PSsetgray(NX_WHITE);
  113. //    NXRectFill(&t);
  114.     [cellCacheContentView getBounds:&cellCacheBounds];
  115.     [activeCell drawSelf:&cellCacheBounds inView:cellCacheContentView];
  116.     [cellCacheContentView unlockFocus];
  117.  
  118.   /* save the mouse's location relative to the cell's origin */
  119.     dy = mouseDownLocation.y - cellFrame.origin.y;
  120.     
  121.   /* from now on we'll be drawing into ourself */
  122.     [self lockFocus];
  123.     
  124.     event = theEvent;
  125.     while (event->type != NX_MOUSEUP) {
  126.       
  127.       /* erase the active cell using the image in the matrix cache */
  128.     [self getVisibleRect:&visibleRect];
  129.     PScomposite(NX_X(&cellFrame), NX_HEIGHT(&visibleRect) -
  130.             NX_Y(&cellFrame) + NX_Y(&visibleRect) -
  131.             NX_HEIGHT(&cellFrame), NX_WIDTH(&cellFrame),
  132.             NX_HEIGHT(&cellFrame), [matrixCache gState],
  133.             NX_X(&cellFrame), NX_Y(&cellFrame) + NX_HEIGHT(&cellFrame),
  134.             NX_COPY);
  135.     
  136.       /* move the active cell */
  137.     mouseLocation = event->location;
  138.     [self convertPoint:&mouseLocation fromView:nil];
  139.     cellFrame.origin.y = mouseLocation.y - dy;
  140.     
  141.       /* constrain the cell's location to our bounds */
  142.     if (NX_Y(&cellFrame) < NX_X(&bounds) ) {
  143.         cellFrame.origin.y = NX_X(&bounds);
  144.     } else if (NX_MAXY(&cellFrame) > NX_MAXY(&bounds)) {
  145.         cellFrame.origin.y = NX_HEIGHT(&bounds) - NX_HEIGHT(&cellFrame);
  146.     }
  147.  
  148.       /*
  149.        * make sure the cell will be entirely visible in its new location (if
  150.        * we're in a scrollView, it may not be)
  151.        */
  152.     if (!NXContainsRect(&visibleRect, &cellFrame) && mFlags.autoscroll) {    
  153.       /*
  154.        * the cell won't be entirely visible, so scroll, dood, scroll, but
  155.        * don't display on-screen yet
  156.        */
  157.         [window disableFlushWindow];
  158.         [self scrollRectToVisible:&cellFrame];
  159.         [window reenableFlushWindow];
  160.         
  161.       /* copy the new image to the matrix cache */
  162.         [matrixCacheContentView lockFocus];
  163.         [self getVisibleRect:&visibleRect];
  164.         [self convertRectFromSuperview:&visibleRect];
  165.         [self convertRect:&visibleRect toView:nil];
  166.         PScomposite(NX_X(&visibleRect), NX_Y(&visibleRect),
  167.             NX_WIDTH(&visibleRect), NX_HEIGHT(&visibleRect),
  168.             [window gState], 0.0, NX_HEIGHT(&visibleRect),
  169.             NX_COPY);
  170.         [matrixCacheContentView unlockFocus];
  171.         
  172.       /*
  173.        * note that we scrolled and start generating timer events for
  174.        * autoscrolling
  175.        */
  176.         scrolled = YES;
  177.         startTimer(timer);
  178.     } else {
  179.       /* no scrolling, so stop any timer */
  180.         stopTimer(timer);
  181.     }
  182.       
  183.       /* composite the active cell's image on top of ourself */
  184.     PScomposite(0.0, 0.0, NX_WIDTH(&cellFrame), NX_HEIGHT(&cellFrame),
  185.             [cellCache gState], NX_X(&cellFrame),
  186.             NX_Y(&cellFrame) + NX_HEIGHT(&cellFrame), NX_COPY);
  187.     
  188.       /* now show what we've done */
  189.     [window flushWindow];
  190.     
  191.       /*
  192.        * if we autoscrolled, flush any lingering window server events to make
  193.        * the scrolling smooth
  194.        */
  195.     if (scrolled) {
  196.         NXPing();
  197.         scrolled = NO;
  198.     }
  199.     
  200.       /* save the current mouse location, just in case we need it again */
  201.     mouseLocation = event->location;
  202.     
  203.     if (![NXApp peekNextEvent:MOVE_MASK into:&peek]) {
  204.       /*
  205.        * no mouseMoved or mouseUp event immediately avaiable, so take
  206.        * mouseMoved, mouseUp, or timer
  207.        */
  208.         event = [NXApp getNextEvent:MOVE_MASK|NX_TIMERMASK];
  209.     } else {
  210.       /* get the mouseMoved or mouseUp event in the queue */
  211.         event = [NXApp getNextEvent:MOVE_MASK];
  212.     }
  213.     
  214.       /* if a timer event, mouse location isn't valid, so we'll set it */
  215.     if (event->type == NX_TIMER) {
  216.         event->location = mouseLocation;
  217.     }
  218.     }
  219.     
  220.   /* mouseUp, so stop any timer and unlock focus */
  221.     stopTimer(timer);
  222.     [self unlockFocus];
  223.     
  224.   /* find the cell under the mouse's location */
  225.     mouseUpLocation = event->location;
  226.     [self convertPoint:&mouseUpLocation fromView:nil];
  227.     if (![self getRow:&newRow andCol:&column forPoint:&mouseUpLocation]) {
  228.       /* mouse is out of bounds, so find the cell the active cell covers */
  229.     [self getRow:&newRow andCol:&column forPoint:&(cellFrame.origin)];
  230.     }
  231.     
  232.   /* we need to shuffle cells if the active cell's going to a new location */
  233.     if (newRow != row) {
  234.       /* no autodisplay while we move cells around */
  235.     [self setAutodisplay:NO];
  236.     if (newRow > row) {
  237.       /* adjust selected row if before new active cell location */
  238.         if (selectedRow <= newRow) {
  239.         selectedRow--;
  240.         }
  241.     
  242.       /*
  243.        * push all cells above the active cell's new location up one row so
  244.        * that we fill the vacant spot
  245.        */
  246.         while (row++ < newRow) {
  247.         cell = [self cellAt:row :0];
  248.         [self putCell:cell at:(row - 1) :0];
  249.         }
  250.       /* now place the active cell in its new home */
  251.         [self putCell:activeCell at:newRow :0];
  252.     } else if (newRow < row) {
  253.           /* adjust selected row if after new active cell location */
  254.         if (selectedRow >= newRow) {
  255.         selectedRow++;
  256.         }
  257.     
  258.       /*
  259.        * push all cells below the active cell's new location down one row
  260.        * so that we fill the vacant spot
  261.        */
  262.         while (row-- > newRow) {
  263.         cell = [self cellAt:row :0];
  264.         [self putCell:cell at:(row + 1) :0];
  265.         }
  266.       /* now place the active cell in its new home */
  267.         [self putCell:activeCell at:newRow :0];
  268.     }
  269.       
  270.       /* if the active cell is selected, note its new row */
  271.     if ([activeCell state]) {
  272.         selectedRow = newRow;
  273.     }
  274.       
  275.       /* make sure the active cell's visible if we're autoscrolling */
  276.     if (mFlags.autoscroll) {
  277.         [self scrollCellToVisible:newRow :0];
  278.     }
  279.       
  280.       /* no longer dragging the cell */
  281.     activeCell = 0;
  282.     
  283.       /* size to cells after all this shuffling and turn autodisplay back on */
  284.     [[self sizeToCells] setAutodisplay:YES];
  285.     } else {
  286.       /* no longer dragging the cell */
  287.     activeCell = 0;
  288.     }
  289.     
  290.   /* now redraw ourself */
  291.     [self display];
  292.     
  293.   /* set the event mask to normal */
  294.     [window setEventMask:eventMask];
  295.  
  296.     return self;
  297. }
  298.  
  299. - drawSelf:(NXRect *)rects :(int)count
  300. {
  301.     int        row, col;
  302.     NXRect    cellBorder;
  303.     int        sides[] = {NX_XMIN, NX_YMIN, NX_XMAX, NX_YMAX, NX_XMIN,
  304.                    NX_YMIN};
  305.     float    grays[] = {NX_DKGRAY, NX_DKGRAY, NX_WHITE, NX_WHITE, NX_BLACK,
  306.                NX_BLACK};
  307.                
  308.   /* do the regular drawing */
  309.     [super drawSelf:rects :count];
  310.     
  311.   /* draw a "well" if the user's dragging a cell */
  312.     if (activeCell) {
  313.       /* get the cell's frame */
  314.     [self getRow:&row andCol:&col ofCell:activeCell];
  315.     [self getCellFrame:&cellBorder at:row :col];
  316.       
  317.       /* draw the well */
  318.     if (NXIntersectsRect(&cellBorder, &(rects[0]))) {
  319.         NXDrawTiledRects(&cellBorder, (NXRect *)0, sides, grays, 6);
  320.         PSsetgray(0.17);
  321.         NXRectFill(&cellBorder);
  322.     }
  323.     }
  324.     
  325.     return self;
  326. }
  327.  
  328. - setupCacheWindows
  329. {
  330.     NXRect    visibleRect;
  331.  
  332.   /* create the matrix cache window */
  333.     [self getVisibleRect:&visibleRect];
  334.     matrixCache = [self sizeCacheWindow:matrixCache to:&(visibleRect.size)];
  335.     
  336.   /* create the cell cache window */
  337.     cellCache = [self sizeCacheWindow:cellCache to:&cellSize];
  338.  
  339.     return self;
  340. }
  341.  
  342. - sizeCacheWindow:cacheWindow to:(NXSize *)windowSize
  343. {
  344.     NXRect    cacheFrame;
  345.     
  346.     if (!cacheWindow) {
  347.       /* create the cache window if it doesn't exist */
  348.     cacheFrame.origin.x = cacheFrame.origin.y = 0.0;
  349.     cacheFrame.size = *windowSize;
  350.     cacheWindow = [[[Window alloc] initContent:&cacheFrame
  351.                        style:NX_PLAINSTYLE
  352.                        backing:NX_RETAINED
  353.                        buttonMask:0
  354.                        defer:NO] reenableDisplay];
  355.       /* flip the contentView since we are flipped */
  356.     [[cacheWindow contentView] setFlipped:YES];
  357.     } else {
  358.       /* make sure the cache window's the right size */
  359.     [cacheWindow getFrame:&cacheFrame];
  360.     if (cacheFrame.size.width != windowSize->width ||
  361.               cacheFrame.size.height != windowSize->height) {
  362.         [cacheWindow sizeWindow:windowSize->width
  363.                        :windowSize->height];
  364.     }
  365.     }
  366.     
  367.     return cacheWindow;
  368. }
  369. - (BOOL)controlDrag
  370. {
  371.   return controlDrag;
  372. }
  373.  
  374. - setControlDrag:(BOOL) asdf
  375. {
  376.   controlDrag=asdf;
  377.   return self;
  378. }
  379.  
  380. @end
  381.